12. Solution: Dynamic Proxy

Solution: Dynamic Proxy

ND079 JPND C2 L04 A11 Solution Dynamic Proxy

UdacisearchClientFactory.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public final class UdacisearchClientFactory {

  public static UdacisearchClient fromPropertyMap(Map<String, Object> properties) {
    Object proxy =
        Proxy.newProxyInstance(
            UdacisearchClientFactory.class.getClassLoader(),
            new Class<?>[]{UdacisearchClient.class},
            new Handler(properties));
    return (UdacisearchClient) proxy;
  }

  private UdacisearchClientFactory() {
    // UdacisearchClientFactory cannot be instantiated.
  }

  private static final class Handler implements InvocationHandler {

    private final Map<String, Object> properties;

    private Handler(Map<String, Object> properties) {
      this.properties = new HashMap<>(Objects.requireNonNull(properties));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      String methodName = method.getName();
      if (method.getDeclaringClass().equals(Object.class)) {
        // Invoke toString() and hashCode() directly on the property Map.
        try {
          return method.invoke(properties, args);
        } catch (InvocationTargetException e) {
          throw e.getTargetException();
        } catch (IllegalAccessException e) {
          throw new RuntimeException(e);
        }
      }
      if (methodName.length() <= 3 || !methodName.startsWith("get")) {
        throw new RuntimeException("Method is not a property getter: " + methodName);
      }
      String propertyName = methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
      if (!properties.containsKey(propertyName)) {
        throw new RuntimeException("No property named \"" + propertyName + "\" found in map.");
      }
      return properties.get(propertyName);
    }
  }
}